package com.dogantekin.allseeingeye.eye;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.location.Location;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;

import com.dogantekin.allseeingeye.IConstants;
import com.dogantekin.allseeingeye.data.DataFactory;
import com.dogantekin.allseeingeye.map.Map;
import com.dogantekin.allseeingeye.sensor.ISensorListener;
import com.dogantekin.allseeingeye.sensor.SensorFactory;
import com.dogantekin.allseeingeye.util.LocationItem;
import com.dogantekin.allseeingeye.R;

public class Eye extends Activity implements IConstants, ISensorListener {
	private static final int ID_CAMERA_VIEW  = 1;
	private static final int ID_DRAWING_VIEW = 2;
	private static final int MENU_MAP_VIEW   = 1;
	private static final int MENU_EXIT       = 2;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		final CustomCameraView cameraView = new CustomCameraView(this);
		final DrawingView drawingView = new DrawingView(this);
		final FrameLayout frameLayout = new FrameLayout(this);
		frameLayout.addView(cameraView);
		
		drawingView.setRange(Integer.parseInt(getResources().getString(R.string.range)));
		drawingView.setEnabled(true);
		drawingView.setClickable(true);
		drawingView.setFocusable(true);
		drawingView.setFocusableInTouchMode(true);
		drawingView.setSelected(true);
		drawingView.setOnTouchListener(new View.OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					drawingView.touched(event.getX(), event.getY());
					return true;
				}
				return false;
			}
		});
		Location lastLocation = SensorFactory.getInstance(this).getLastKnownLocation();
		drawingView.setLocation(lastLocation);
		drawingView.setLocationItems(DataFactory.getInstance(this).getLocationItems(lastLocation));
		frameLayout.addView(drawingView);
		setContentView(frameLayout);
		SensorFactory.getInstance(this).addListener(this);
	}

	@Override
	public void updateLocation(Location location) {
		DrawingView drawingView = (DrawingView) findViewById(ID_DRAWING_VIEW);
		drawingView.setLocation(location);
		drawingView.setLocationItems(DataFactory.getInstance(this).getLocationItems(location));
		drawingView.postInvalidate();
	}

	@Override
	public void updateOrientation(int azimuth, int pitch, int roll) {
		DrawingView drawingView = (DrawingView) findViewById(ID_DRAWING_VIEW);
		drawingView.setAzimuth(azimuth);
		drawingView.setPitch(pitch);
		drawingView.setRoll(roll);
		drawingView.postInvalidate();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuItem mapView = menu.add(0, MENU_MAP_VIEW, 0, getResources().getString(R.string.menu_map_view));
		mapView.setIcon(android.R.drawable.ic_menu_mapmode);
		MenuItem exit    = menu.add(0, MENU_EXIT, 1, getResources().getString(R.string.menu_exit));
		exit.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case MENU_MAP_VIEW:
			Intent mapIntent = new Intent(this,Map.class);
			startActivity(mapIntent);
			finish();
			break;
		case MENU_EXIT:
			finish();
			break;
		}
		return true;
	}

	private class DrawingView extends View {
		private int 					azimuth = 0;
		private int 					pitch   = -90;
		private int 					roll 	= 0;
		private Location 				location;
		private ArrayList<LocationItem> locationItems;
		private LocationItem 			selectedItem;
		private int 					infoBarHeight = 60;
		private double 					range;

		public DrawingView(Context context) {
			super(context);
			setId(ID_DRAWING_VIEW);
		}

		public void setAzimuth(int azimuth) {
			this.azimuth = azimuth;
		}

		public void setPitch(int pitch) {
			this.pitch = pitch;
		}

		public void setRoll(int roll) {
			this.roll = roll;
		}

		public void setLocation(Location location) {
			this.location = location;
		}

		public void setLocationItems(ArrayList<LocationItem> locationItems) {
			this.locationItems = locationItems;
			postInvalidate();
		}

		public void setRange(double range) {
			this.range = range;
		}

		@Override
		protected void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			drawHorizon(canvas);
			drawLocationItems(canvas);
			drawInformationBar(canvas);
		}

		private double getAzimuthAngle() {
			return (450 - azimuth) % 359;
		}

		private void drawHorizon(Canvas canvas) {
			int height        = getMeasuredHeight() - infoBarHeight;
			int width         = getMeasuredWidth();
			int angleOfSight  = Integer.parseInt(getResources().getString(R.string.angleOfSight));
			Paint horizonLine = new Paint(Paint.ANTI_ALIAS_FLAG);
			horizonLine.setColor(Color.GREEN);
			horizonLine.setTextSize(30);

			double azimuthShownLimit1 = azimuth - (angleOfSight / 2);
			if (azimuthShownLimit1 < 0) {
				azimuthShownLimit1 = azimuthShownLimit1 + 360;
			}
			double azimuthShownLimit2 = azimuth + (angleOfSight / 2);
			if (azimuthShownLimit2 >= 360) {
				azimuthShownLimit2 = azimuthShownLimit2 - 360;
			}
			double xPerDegree = width / angleOfSight;
			if ((azimuthShownLimit2 - angleOfSight >= 0 ? 0 >= azimuthShownLimit1 : 360 >= azimuthShownLimit1) && 0 <= azimuthShownLimit2) {
				// check north
				drawTextToMiddle(canvas, getResources().getString(R.string.north),(float) (width - (azimuthShownLimit2 * xPerDegree)),(float) height / 2, horizonLine);
			} else if (90 >= azimuthShownLimit1 && 90 <= azimuthShownLimit2) {
				// check east
				drawTextToMiddle(canvas,getResources().getString(R.string.east),(float) (width - ((azimuthShownLimit2 - 90) * xPerDegree)),(float) height / 2, horizonLine);
			} else if (180 >= azimuthShownLimit1 && 180 <= azimuthShownLimit2) {
				// check south
				drawTextToMiddle(canvas,getResources().getString(R.string.south),(float) (width - ((azimuthShownLimit2 - 180) * xPerDegree)),(float) height / 2, horizonLine);
			} else if (270 >= azimuthShownLimit1 && 270 <= azimuthShownLimit2) {
				// check west
				drawTextToMiddle(canvas,getResources().getString(R.string.west),(float) (width - ((azimuthShownLimit2 - 270) * xPerDegree)),(float) height / 2, horizonLine);
			}

			canvas.drawLine(0, height / 2, width, height / 2, horizonLine);
		}

		private void drawLocationItems(Canvas canvas) {
			double azimuthAngle = getAzimuthAngle();
			int    angleOfSight = Integer.parseInt(getResources().getString(R.string.angleOfSight));
			int    height       = getMeasuredHeight() - infoBarHeight;
			int    width        = getMeasuredWidth();
			Paint  outerPaint        = new Paint(Paint.ANTI_ALIAS_FLAG);
			outerPaint.setARGB(200, 255, 132, 14);
			Paint  innerPaint        = new Paint(Paint.ANTI_ALIAS_FLAG);
			innerPaint.setARGB(200, 255, 222, 38);
			Paint selectedOuterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			selectedOuterPaint.setARGB(200, 0, 49, 56);
			Paint selectedInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			selectedInnerPaint.setARGB(200, 0, 110, 89);
			if (locationItems != null) {
				double xPerDegree = width / angleOfSight;
				double yPerDistance = (height / 2) / range;
				for (LocationItem locationItem : locationItems) {
					if (inAreaOfSight(azimuthAngle, locationItem)) {
						double tempX = Double.MIN_VALUE;
						double tempY = height - yPerDistance* locationItem.getDistance();
						if (locationItem.isLeft()) {
							tempX = (width / 2) - (xPerDegree * locationItem.getAngleBetween());
						} else {
							tempX = (width / 2) + (xPerDegree * locationItem.getAngleBetween());
						}
						locationItem.setVisible(true);
						locationItem.setScreenX((float) tempX);
						locationItem.setScreenY((float) tempY);
						if (locationItem.isSelected()) {
							canvas.drawCircle(locationItem.getScreenX(), locationItem.getScreenY(), 15, selectedOuterPaint);
							canvas.drawCircle(locationItem.getScreenX(), locationItem.getScreenY(), 13, selectedInnerPaint);
						} else {
							canvas.drawCircle(locationItem.getScreenX(), locationItem.getScreenY(), 15, outerPaint);
							canvas.drawCircle(locationItem.getScreenX(), locationItem.getScreenY(), 13, innerPaint);
						}
					}
				}
			}
		}

		private boolean inAreaOfSight(double azimuthAngle, LocationItem locationItem) {
			locationItem.setDistance(locationItem.calculateDistance(location));
			if(locationItem.getDistance() > Integer.parseInt(getResources().getString(R.string.range))) {
				return false;
			} else {
				int 		 angleOfSight  = Integer.parseInt(getResources().getString(R.string.angleOfSight));
				double[] 	 azimuthVector = null;
				if (azimuthAngle == 0) {
					azimuthVector = new double[] { 1, 0 };
				} else if (azimuthAngle == 90) {
					azimuthVector = new double[] { 0, 1 };
				} else if (azimuthAngle == 180) {
					azimuthVector = new double[] { -1, 0 };
				} else if (azimuthAngle == 270) {
					azimuthVector = new double[] { 0, -1 };
				} else if ((azimuthAngle > 0 && azimuthAngle < 90) || (azimuthAngle > 270 && azimuthAngle < 360)) {
					azimuthVector = new double[] { 1, 1 * Math.tan(Math.toRadians(azimuthAngle)) };
				} else {
					azimuthVector = new double[] { -1, -1 * Math.tan(Math.toRadians(azimuthAngle)) };
				}
				double azimuthLength    = Math.sqrt(Math.pow(azimuthVector[0], 2)+ Math.pow(azimuthVector[1], 2));
				azimuthVector[0] 	    = azimuthVector[0] / azimuthLength;
				azimuthVector[1] 	    = azimuthVector[1] / azimuthLength;
				double[] locationVector = new double[] {locationItem.getLongitude() - location.getLongitude(),locationItem.getLatitude() - location.getLatitude() };
				double   locationLength = Math.sqrt(Math.pow(locationVector[0], 2) + Math.pow(locationVector[1], 2));
				locationVector[0]   = locationVector[0] / locationLength;
				locationVector[1]   = locationVector[1] / locationLength;
				double dotProduct   = (azimuthVector[0] * locationVector[0] + azimuthVector[1] * locationVector[1]);
				double angleBetween = Math.toDegrees(Math.acos(dotProduct));
				if (angleBetween <= angleOfSight / 2) {
					double tempAngle = Math.toDegrees(Math.atan2(locationVector[1],locationVector[0]));
					if (locationVector[1] < 0) {
						tempAngle = 360 + tempAngle;
					}
					double leftTotal   = (azimuthAngle+angleBetween) % 359;
					double rightTotal  = (azimuthAngle-angleBetween) % 359;
					if(leftTotal < 0) {
						leftTotal = leftTotal + 360;
					}
					if(rightTotal < 0) {
						rightTotal = rightTotal + 360;
					}
					double rightDelta = Math.abs(tempAngle-rightTotal);
					double leftDelta  = Math.abs(tempAngle-leftTotal);
					if(rightDelta>=leftDelta) {
						locationItem.setLeft(true);					
					} else {
						locationItem.setLeft(false);					
					}
					locationItem.setAngleBetween(angleBetween);
					return true;
				}
				return false;				
			}
		}

		private void drawInformationBar(Canvas canvas) {
			Paint informationBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
			informationBackground.setARGB(200, 170, 194, 41);
			Paint informationText = new Paint(Paint.ANTI_ALIAS_FLAG);
			informationText.setColor(Color.WHITE);

			int height = getMeasuredHeight();
			int width  = getMeasuredWidth();

			canvas.drawRect(0, height - infoBarHeight, width, height,informationBackground);
			String latitudeStr = "";
			String longitudeStr = "";
			String azimuthStr = "Azimuth : " + azimuth;
			String pitchStr = "Pitch : " + pitch;
			String rollStr = "Roll : " + roll;
			if (location == null) {
				latitudeStr = "Latitude unknown";
				longitudeStr = "Longitude unknown";
			} else {
				latitudeStr = "Latitude : " + location.getLatitude();
				longitudeStr = "Longitude : " + location.getLongitude();
			}
			canvas.drawText(latitudeStr, 0, height - 50, informationText);
			canvas.drawText(longitudeStr, 0, height - 40, informationText);
			canvas.drawText(azimuthStr, 0, height - 30, informationText);
			canvas.drawText(pitchStr, 0, height - 20, informationText);
			canvas.drawText(rollStr, 0, height - 10, informationText);

			if (selectedItem != null) {
				canvas.drawText(selectedItem.getName(), width / 2 +40, height - 50,informationText);
				canvas.drawText(selectedItem.getPhone(), width / 2+40,height - 40, informationText);
				canvas.drawText(selectedItem.getUrl(), width / 2+40, height - 30,informationText);
				canvas.drawText(String.valueOf((int)selectedItem.getDistance())+"mt",width / 2+40, height - 20, informationText);
				if(selectedItem.getImage() != null) {
					canvas.drawBitmap(selectedItem.getImage(), null, new Rect(width/2-30,height-infoBarHeight,width/2+30,height), informationText);				
				}
			}
		}

		public void touched(float selectedX, float selectedY) {
			int height = getMeasuredHeight();
			boolean change = false;

			if (selectedY <= height - infoBarHeight) {
				selectedItem = null;
				for (LocationItem locationItem : locationItems) {
					if (Math.sqrt(Math.pow(selectedX - locationItem.getScreenX(), 2) + Math.pow(selectedY - locationItem.getScreenY(),2)) <= 15) {
						if (!locationItem.isSelected()) {
							change = true;
						}
						locationItem.setSelected(true);
						selectedItem = locationItem;
					} else {
						if (locationItem.isSelected()) {
							change = true;
						}
						locationItem.setSelected(false);
					}
				}
			}
			if (change) {
				postInvalidate();
			}
		}

		private void drawTextToMiddle(Canvas canvas, String text, float x, float y, Paint paint) {
			float textLength = paint.measureText(text);
			canvas.drawText(text, x + (textLength / 2), y, paint);
		}
	}

	private class CustomCameraView extends SurfaceView implements
			SurfaceHolder.Callback {
		SurfaceHolder holder;
		Camera camera;

		public CustomCameraView(Context context) {
			super(context);
			setId(ID_CAMERA_VIEW);
			holder = this.getHolder();
			holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
			holder.addCallback(this);
		}

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			camera = Camera.open();
			try {
				camera.setPreviewDisplay(holder);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			Parameters params = camera.getParameters();
			params.setPreviewSize(width, height);
			params.setPictureFormat(PixelFormat.JPEG);
			params.set("orientation", "portrait");
			camera.setParameters(params);
			camera.startPreview();
			// Canvas canvas = holder.lockCanvas();
			// Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
			// paint.setColor(Color.WHITE);
			// canvas.drawCircle(canvas.getWidth()/2, canvas.getHeight()/2, 20,
			// paint);
			// holder.unlockCanvasAndPost(canvas);
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			camera.stopPreview();
			camera.release();
		}
	}
}